package itsm.isperp.framework.web.request;

import itsm.isperp.framework.data.ObjectSqlSource;
import itsm.isperp.framework.data.dialect.DialectFactory;
import itsm.isperp.framework.data.domain.FilterRule;

import java.io.Serializable;
import java.util.Collection;

import me.chanjar.weixin.common.util.StringUtils;

import org.springframework.data.jdbc.support.DatabaseType;

import com.fasterxml.jackson.annotation.JsonIgnore;

/**
 * 
 * JqGrid过滤器({@link JqGridFilter} )的过滤规则，继承对象映射sql接口( {@link ObjectSqlSource})
 * 可直接转为有效的sql片段。 用于定义过滤器原始数据格式
 * {"groupOp":"AND","rules":[{"field":"age","op":"le","data":"30"}]}中rules部分。
 * <p>
 * field：表示条件的依据名称。
 * </p>
 * op({@link JqGridFilterRuleOp})
 * ：表示条件（eq=等于，ne=不等，lt=小于，le=小于等于，gt=大于，ge=大于等于，bw=开始于，bn=不开始于，in=在，ni=不在内，\
 * ew=结束于，en=不结束于，cn=包含，nc=不包含）。 </p>
 * <p>
 * data：表示的条件值。
 * </p>
 * 
 * 
 * @author lizx
 * @date 2013-1-24
 * @version V1.0
 * @version V1.1 完善构造器
 */
public final class JqGridFilterRule implements Serializable, FilterRule {

	/**
	 * 
	 */
	private static final long serialVersionUID = 2311928125457183457L;

	private String junction;
	private String field;
	/**
	 * * op({@link JqGridFilterRuleOp})
	 * ：表示条件（eq=等于，ne=不等，lt=小于，le=小于等于，gt=大于，ge=
	 * 大于等于，bw=开始于，bn=不开始于，in=在，ni=不在内，\ ew=结束于，en=不结束于，cn=包含，nc=不包含）。 </p>
	 */
	private JqGridFilterRuleOp op;
	private Object data;
	private int type;// 0：字符串；1：数字；2：日期；3：时间；4：时分秒

	/**
	 * 默认构造器
	 */
	public JqGridFilterRule() {
		this.field = "1";
		this.op = JqGridFilterRuleOp.eq;
		this.data = "1";
	}

	/**
	 * 指定查询字段，查询方式以及查询内容构造过滤器条件
	 * 
	 * @param field
	 *            查询字段
	 * @param op
	 *            查询方式
	 * @param data
	 *            查询类容
	 */
	public JqGridFilterRule(String field, JqGridFilterRuleOp op, String data) {
		this.field = field;
		this.op = op;
		this.data = data;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see itsm.isperp.framework.data.mapping.ObjectSqlSource
	 */
	@JsonIgnore
	@Override
	public String getSql() {
		StringBuilder sb = new StringBuilder();
		String field = itsm.isperp.framework.utils.StringUtils
				.underscore(this.field);

		switch (this.op) {
		case cn:
			appendSql(sb, field);
			break;
		case kcn:
			appendSql(sb, field);
			break;
		case cnx:
			appendSql(sb, field);
			break;
		case eq:
			appendEqSql(sb, field);
			break;
		case ne:
			sb.append(" (");
			sb.append(field).append(" ")
					.append(String.format(this.op.toString(), data));

			if ("".equals(data)) {
				sb.append(" OR ").append(field).append(" IS NOT NULL ");
			} else {
				sb.append(" OR ").append(field).append(" IS NULL ");
			}
			sb.append(" ) ");
			break;
		case isnull:
			if (DialectFactory.getDatabaseType() == DatabaseType.MYSQL) {
				sb.append(" (");
				sb.append(field).append(" ").append(this.op.toString());
				sb.append(" OR ").append(field).append(" ='') ");
			} else {
				sb.append(" (");
				sb.append(field).append(" ").append(this.op.toString())
						.append(") ");
			}
			break;
		case isnotnull:
			if (DialectFactory.getDatabaseType() == DatabaseType.MYSQL) {
				sb.append(" (");
				sb.append(field).append(" <>'') ");
			} else {
				sb.append(" (");
				sb.append(field).append(" ").append(this.op.toString())
						.append(" ) ");
			}

			break;
		case lt:
			appendSqlConditionL(sb, field);
			break;
		case gt:
			appendSqlCondition(sb, field);
			break;
		case le:

			appendSqlConditionL(sb, field);
			break;
		case ge:
			appendSqlCondition(sb, field);

			break;
		default:
			appendSql(sb, field);
			break;
		}

		return sb.toString();
	}

	private void appendEqSql(StringBuilder sb, String field) {
		if (data == null) {
			return;
		}
		if (data instanceof String) {
			appendSql(sb, field, this.op, data.toString());
		} else if (data instanceof Collection) {

			Collection<?> c = (Collection<?>) data;
			String inV = "";
			for (Object s : c) {
				if (s != null && StringUtils.isNotEmpty(s.toString())) {
					inV += " AND " + field + "='" + s.toString() + "'";
				}
			}

			if (!"".endsWith(inV)) {
				appendSql(sb, this.field, JqGridFilterRuleOp.eq,
						inV.substring(1));
			}
		}

	}

	private void appendSqlConditionL(StringBuilder sb, String field) {

		if (type == 3) {
			if (DialectFactory.getDatabaseType() == DatabaseType.ORACLE) {
				sb.append(field)
						.append(" ")
						.append(String.format(
								this.op.toString().replaceAll("'", ""),
								"TO_DATE('" + data
										+ " 23:59','yyyy-MM-dd hh24:mi')"))
						.append(" ");
			} else {
				sb.append(field)
						.append(" ")
						.append(String.format(
								this.op.toString().replaceAll("'", ""), "'"
										+ data + " 23:59'")).append(" ");
			}

		} else {
			appendSql(sb, field);
		}
	}

	private void appendSqlCondition(StringBuilder sb, String field) {

		if (type == 3) {
			if (DialectFactory.getDatabaseType() == DatabaseType.ORACLE) {
				sb.append(field)
						.append(" ")
						.append(String.format(
								this.op.toString().replaceAll("'", ""),
								"TO_DATE('" + data + "','yyyy-MM-dd')"))
						.append(" ");
			} else {
				sb.append(field)
						.append(" ")
						.append(String.format(
								this.op.toString().replaceAll("'", ""), "'"
										+ data + "'")).append(" ");
			}

		} else {

			appendSql(sb, field);
		}

	}

	private void appendSql(StringBuilder sb, String field) {
		if (data == null) {
			return;
		}
		if (data instanceof String) {
			appendSql(sb, field, this.op, data.toString());
		} else if (data instanceof Collection) {

			Collection<?> c = (Collection<?>) data;
			String inV = "";
			for (Object s : c) {
				if (s != null && StringUtils.isNotEmpty(s.toString())) {
					inV += ",'" + s.toString() + "'";
				}
			}

			if (!"".endsWith(inV)) {
				appendSql(sb, this.field, JqGridFilterRuleOp.in,
						inV.substring(1));
			}
		}
	}

	private static void appendSql(StringBuilder sb, String field,
			JqGridFilterRuleOp op, String data) {
		if (op != JqGridFilterRuleOp.eq && StringUtils.isEmpty(data)) {
			return;
		}

		if (field.toLowerCase().indexOf("code") >= 0) {
			data = data.toUpperCase();
		}
		if (field.indexOf(",") > 0) {

			String[] arr = field.split(",");
			sb.append(" ( ").append(arr[0]).append(" ")
					.append(String.format(op.toString(), data));

			String ala = "";
			int idx = arr[0].indexOf(".");
			if (idx > 0) {
				ala = arr[0].substring(0, idx + 1);
			}

			for (int i = 1; i < arr.length; i++) {
				if (arr[i].contains(".")) {
					sb.append(" OR ").append(arr[i]).append(" ")
							.append(String.format(op.toString(), data));
				} else {
					sb.append(" OR ").append(ala).append(arr[i]).append(" ")
							.append(String.format(op.toString(), data));
				}
			}
			sb.append(" ) ");

		} else {
			sb.append(field).append(" ")
					.append(String.format(op.toString(), data)).append(" ");
		}
	}

	@Override
	public String toString() {
		return "{\"field\":\"" + this.field + "\",\"op\":\"" + this.op.name()
				+ "\",\"data:\"" + this.data + "\"}";
	}

	public String getJunction() {
		return junction;
	}

	public void setJunction(String junction) {
		this.junction = junction;
	}

	@Override
	public String getField() {
		return field;
	}

	public void setField(String field) {
		this.field = field;
	}

	public JqGridFilterRuleOp getOp() {
		return op;
	}

	public void setOp(JqGridFilterRuleOp op) {
		this.op = op;
	}

	public String getData() {
		if (data == null) {
			return null;
		}
		return data.toString();
	}

	public void setData(Object data) {
		this.data = data;
	}

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

}
